--[[


THIS FILE CONTAINS ALL THE THREADING CODE


]]

--Simple function to handle callbacks for queries that need none.
local function generalcallback()

end


--this function queries the database index provided, puts the threadid in the database table, calls the timer responsible for the thread after creation
--returns false on error(usually because of invalid type) false if database is nonexistant or suspended , true if it works
function gdatabase.ThreadedQuery(strQuery, intDatabaseIndex, fnCallback, ...)
	fnCallback = fnCallback or generalcallback
	if (type(strQuery) ~= "string" or type(intDatabaseIndex) ~= "number" or type(fnCallback) ~= "function") then
		return false	
	end
	--checks to make sure the database exists	
	if(!gdatabase.CheckForConnection(intDatabaseIndex) or intDatabaseIndex == nil) then
		Msg("GDATABASE: Threaded Query Failure: INDEX DOESN'T EXIST OR TIMEDOUT!\n")
		return false
	end
	
	if(!gdatabase.connections[intDatabaseIndex].status) then
		return false
	end
	
	--make the table
	local tblSettings = gdatabase.connections[intDatabaseIndex].settings
	local tblQuery = {}
	local tblcount = table.getn(gdatabase.connections[intDatabaseIndex].threads.queue)
	local strTimerId = "gdatabasethreadqueue" .. tostring(intDatabaseIndex)
	if tblcount >= tblSettings["maxqueuesize"] then return false end --max size of the queue stack
	
	
	tblQuery["query"] = strQuery
	tblQuery["callback"] = fnCallback
	tblQuery["arguments"] = arg or 0
	table.insert(gdatabase.connections[intDatabaseIndex].threads.queue, 1,  tblQuery)

	if tblcount == 0 then --the queue is empty so we need to start the timer again
		if ptimer.IsTimer(strTimerId) then --Does it Exist?
			ptimer.UnPause(strTimerId)
		else 
			ptimer.Create(strTimerId,tblSettings["threadqueuetime"],0,gdatabase.checkthreadqueue, intDatabaseIndex) --No timer made yet, so we made one.
		end
	end
	
	return true --somehow I forgot this
end

gdatabase.threadedquery = gdatabase.ThreadedQuery --For compatibility with versions 1.2 and down.

function gdatabase.checkthreadqueue(intDatabaseIndex)
	local tblThreads = gdatabase.connections[intDatabaseIndex].threads
	local tblSettings = gdatabase.connections[intDatabaseIndex].settings
	
	--this optimizes the code so it doesn't do more then it has to.
	local activethreads = table.getn(tblThreads["active"]) 
	local queuedthreads =  table.getn(tblThreads["queue"])
	
	--I put this in here so that we don't have a thread timer checking constantly when its not needed
	if activethreads <= 0 and queuedthreads <= 0 then
		if tblThreads["queuetime"] >= tblSettings["maxnothreadtime"] then 
			local strTimerId = "gdatabasethreadqueue" .. tostring(intDatabaseindex)
			ptimer.Pause(strTimerId)
			tblThreads["queuetime"] = 0
			return
		else
			tblThreads["queuetime"] = tblThreads["queuetime"] + 1
			return
		end
	end
	tblThreads["queuetime"] = 0
	
	--now process the active threads to make room for new ones from the queue.
	local inc = 0
	for k = 1, activethreads do
		inc = inc + 1
		if gdatabase.processthread(tblThreads["active"][inc], intDatabaseIndex) != nil then --returns nil when thread isnt finished
			table.remove(tblThreads["active"],inc)
			inc = inc - 1
		end
	end
	
	--add a new thread to the active table if its below the max threads threshold
	activethreads = table.getn(tblThreads["active"])
	if activethreads < tblSettings["concurrentthreads"] then 
		inc = 0
		queuedthreads = table.getn(tblThreads["queue"]) --I put this in here because we wouldn't need it outside this loop
		if queuedthreads > 0 then
			while ((activethreads + tblThreads["processing"]) < tblSettings["concurrentthreads"] and queuedthreads > 0) do
				ptimer.Simple(inc * .1 + .1, gdatabase.delayedthread, table.remove(tblThreads["queue"]), intDatabaseIndex)
				tblThreads["processing"] = tblThreads["processing"] + 1
				queuedthreads = queuedthreads - 1
				inc = inc + 1
			end
		end
	end
	

end

--this function is calls a thread to be made. 
function gdatabase.delayedthread(theThread, intDatabaseIndex)
	local tblThreads = gdatabase.connections[intDatabaseIndex].threads
	local newThread = gdatabase.makethread(theThread, intDatabaseIndex)
	if newThread != false then
		table.insert(tblThreads["active"],newThread)
	end
	tblThreads["processing"] = tblThreads["processing"] - 1
end

function gdatabase.makethread(tblQuery, intDatabaseIndex)
	local tblReturn = tblQuery
	local thread, isok, error = mysql.query(gdatabase.connections[intDatabaseIndex].database, tblQuery["query"], mysql.QUERY_NOBLOCK);
	if(thread) then --THREAD was successfully made!
		if gdatabase.config.debug then 
			Msg("Thread Created!\n")
		end
	else
		Msg("GDATABASE: THREAD CREATION ERROR:" .. error .. "\n")
		return false
	end
	tblReturn["threadid"] = thread
	tblReturn["timesran"] = 0
	
	return tblReturn
end

function gdatabase.processthread(tblQuery, intDatabaseIndex)
	if (mysql.query_complete(tblQuery["threadid"])) then
		--query is complete
		local result, isok, err = mysql.query_read(tblQuery["threadid"])
		mysql.query_cleanup(tblQuery["threadid"])
		
		if (!isok) then
            Msg( "GDATABASE: Threaded Query Error:" ..  tostring(err) .. "\n");
			ptimer.Simple(0, tblQuery["callback"], 0, unpack(tblQuery["arguments"]))
			
			return false
		end
		
		if result then
			if gdatabase.config.debug then 
				Msg("Thread Finished!\n")
			end
			ptimer.Simple(0, tblQuery["callback"], result, unpack(tblQuery["arguments"]))
		end
		return true
	else
		local tblSettings = gdatabase.connections[intDatabaseIndex].settings
		tblQuery["timesran"] = tblQuery["timesran"] + 1
		if tblQuery["timesran"] >= tblSettings["maxnothreadtime"] then
			gdatabase.lib.removethread(tblQuery)
			if gdatabase.config.debug then
				Msg("GDATABASE: Thread timeout reached removing thread #" .. tblQuery["threadid"] .. "\n")
			end
			
			return false
		else
			return nil
		end
	end
end


















